package core;

import gitobject.Blob;
import gitobject.Commit;
import gitobject.GitObject;
import gitobject.Tree;
import picocli.CommandLine.*;
import repository.Branch;
import repository.Index;
import repository.Repository;
import utility.Utility;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.sql.Time;
import java.util.*;

@Command(
        name = "status"
)
/**
 * 实现了jit status方法，打印出当前仓库状态，包括已修改未添加的内容
 */
public class JitStatus implements Runnable {
    @Override
    public void run() {
        Repository repo = Repository.getInstance();
        try {
            Branch branch = repo.getCurrentBranch();
            System.out.printf("On branch %s%n%n", branch.getName());
            Index index = repo.getIndex();
            var stage = index.getStage();
            Map<Path, Index.Entry> old = new HashMap<>();
            //Changes to be committed
            if (branch.getCommit() != null) {
                Commit commit = (Commit) GitObject.findByKey(branch.getCommit());
                Tree tree = (Tree) GitObject.findByKey(commit.getTree());
                Queue<Tree> treeQueue = new ArrayDeque<>();
                treeQueue.add(tree);
                while (!treeQueue.isEmpty()) {
                    Tree cur = treeQueue.poll();
                    for (GitObject obj : cur.getObjectTree()) {
                        switch (obj.getType()) {
                            case Blob : {
                                Blob blob = (Blob) obj;
                                old.put(blob.getPath(),
                                        new Index.Entry(blob.getKey(), blob.getLastModified()));
                                break;
                            }
                            case Tree: {
                                treeQueue.add((Tree) obj);
                                break;
                            }
                            default: {
                                System.err.println("Invalid Object Type! " + Utility.hex2string(obj.getKey()));
                            }
                        }
                    }
                }
            }
            List<Path> modified = new ArrayList<>();
            List<Path> create = new ArrayList<>();
            for (var record : stage.entrySet()) {
                Path path = record.getKey();
                Index.Entry entry = record.getValue();
                Index.Entry old_entry = old.get(path);
                if (old_entry == null) {
                    create.add(path);
                } else if (old_entry.getLast_access_time() != entry.getLast_access_time()) {
                    // 修改
                    modified.add(path);
                    old.remove(path);
                } else {
                    // 维修改
                    old.remove(path);
                }
            }
            List<Path> delete = old.keySet().stream().toList();
            if (!(create.isEmpty() && modified.isEmpty() && delete.isEmpty())) {
                System.out.println("Changes to be committed:");
                for (Path path : create) {
                    System.out.println("\t\tcreated:\t" + path.toString());
                }
                for (Path path : modified) {
                    System.out.println("\t\tmodified:\t" + path.toString());
                }
                for (Path path : delete) {
                    System.out.println("\t\tdeleted:\t" + path.toString());
                }
            } else {
                System.out.println("Nothing to commit");
            }
            System.out.println();
            // Changes not staged for commit:
            List<Path> notStage = new ArrayList<>();
            for (var item : stage.entrySet()) {
                Path path = item.getKey();
                path = repo.getWorkTree().resolve(path).toAbsolutePath();
                if (item.getValue().getLast_access_time() != path.toFile().lastModified()) {
                    notStage.add(path);
                }
            }
            if (!notStage.isEmpty()) {
                System.out.println("Changes not staged for commit:");
                for (Path path : notStage) {
                    System.out.println("\t\tmodified:\t" + path.toString());
                }
            } else {
                System.out.println("No unstaged change.");
            }
            System.out.println();
            // 未追踪
            System.out.println("Untracked files:");
            Files.walkFileTree(repo.getWorkTree(), new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    if (repo.getWorkTree().relativize(dir).toString().equals(".jit")) {
                        return FileVisitResult.SKIP_SUBTREE;
                    }
                    return super.preVisitDirectory(dir, attrs);
                }

                /**
                 *
                 * @param file  文件路径
                 * @param attrs 参数为文件属性
                 * @return
                 * @throws IOException
                 */
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Path path = repo.getWorkTree().relativize(file);
                    if (!stage.containsKey(path)) {
                        System.out.println("\t\t" + path.toString());
                    }
                    return super.visitFile(file, attrs);
                }
            });
            // Stage
            System.out.println("Current Stage:");
            for (var path : stage.keySet()) {
                System.out.println("\t\t" + path);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
